package com.googlecode.allinclusive.fw.core.ejb.utils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.ejb.EJBHome;
import javax.ejb.EJBLocalHome;
import javax.ejb.EJBLocalObject;
import javax.ejb.EJBObject;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;

import com.googlecode.allinclusive.fw.core.config.GlobalConfig;
import com.googlecode.allinclusive.fw.core.ejb.EJBConfig;
import com.googlecode.allinclusive.fw.core.ejb.EJBException;

@SuppressWarnings({"rawtypes", "unchecked"})
public class EJBUtils {
	
	/**
	 * Invoke EJB
	 * @param jndiName
	 * @param methodName
	 * @param paramTypes
	 * @param params
	 * @return
	 */
	public static Object invokeEJB(String jndiName, String methodName, Class[] paramTypes, Object[] params) {
		return invokeEJB(null, jndiName, methodName, paramTypes, params);
	}
	
	/**
	 * Invoke EJB
	 * @param config
	 * @param jndiName
	 * @param methodName
	 * @param paramTypes
	 * @param params
	 * @return
	 */
	public static Object invokeEJB(EJBConfig config, String jndiName, String methodName, Class[] paramTypes, Object[] params) {
		// Result Object
		Object resultObject = null;
		try {
			EJBObject ejbObject = getInstance(config, jndiName);
			Method method = ejbObject.getClass().getDeclaredMethod(methodName, paramTypes);
			resultObject = method.invoke(ejbObject, params);
		} catch (NoSuchMethodException e) {
			// TODO throw CustomException
			throw new EJBException(e.getMessage(), e);
		} catch (IllegalAccessException e) {
			// TODO throw CustomException
			throw new EJBException(e.getMessage(), e);
		} catch (InvocationTargetException e) {
			// TODO throw CustomException
			throw new EJBException(e.getMessage(), e);
		}
		return resultObject;
	}
	

	/**
	 * Gets the EJBObject for the given JndiName.
	 * @param jndiName
	 * @return
	 */
	public static EJBObject getInstance(String jndiName) {
		return getInstance(null, jndiName);
	}
	
	/**
	 * Gets the EJBObject for the given JndiName.
	 * @param config
	 * @param jndiName
	 * @return
	 */
	public static EJBObject getInstance(EJBConfig config, String jndiName) {
		// EJB Object
        EJBObject instance = null;
		try {
			// Load EJB Configuration
			EJBConfig ejbConfig = config;
			if (ejbConfig == null) {
				ejbConfig = EJBUtils.getDefaultConfig();
			}
			// Create Context
			Context ctx = ejbConfig.createContext();
			// Look EJBHome Interface
			Object homeInterface = ctx.lookup(jndiName);
			// Close Context 
			ctx.close();

			// Get the Home Object
			Object home = PortableRemoteObject.narrow(homeInterface, homeInterface.getClass());
			Class homeClass = home.getClass();
			Method createMethod = homeClass.getDeclaredMethod("create", new Class[0]);
			// Create EJB Object
			instance = (EJBObject) createMethod.invoke(home, new Object[0]);
		} catch (NamingException e) {
			// TODO throw CustomException
			throw new EJBException(e.getMessage(), e);
		} catch (NoSuchMethodException e) {
			// TODO throw CustomException
			throw new EJBException(e.getMessage(), e);
		} catch (IllegalAccessException e) {
			// TODO throw CustomException
			throw new EJBException(e.getMessage(), e);
		} catch (InvocationTargetException e) {
			// TODO throw CustomException
			throw new EJBException(e.getMessage(), e);
		}
		
		return instance;
	}
	
	/**
	 * Gets the EJBHome for the given JndiName.
	 * @param jndiName
	 * @return
	 */
	public static EJBHome getEJBHome(String jndiName) {
		return getEJBHome(null, jndiName);
	}
	
	/**
	 * Gets the EJBHome for the given JndiName.
	 * @param config
	 * @param jndiName
	 * @return
	 */
	public static EJBHome getEJBHome(EJBConfig config, String jndiName) {
		EJBHome home = null;
		try {
			// Load EJB Configuration
			EJBConfig ejbConfig = config;
			if (ejbConfig == null) {
				ejbConfig = EJBUtils.getDefaultConfig();
			}
			Context context = ejbConfig.createContext();
			Object homeInterface = context.lookup(jndiName);
			home = (EJBHome) PortableRemoteObject.narrow(homeInterface, homeInterface.getClass());
			context.close();
		} catch (NamingException namingException) {
			
		}
		return home;
	}
	
	/**
	 * Gets the EJBLocalHome for the given JndiName.
	 * @param jndiName
	 * @return
	 */
	public static EJBLocalHome getEJBLocalHome(String jndiName) {
		return getEJBLocalHome(null, jndiName);
	}
	
	/**
	 * Gets the EJBLocalHome for the given JndiName.
	 * @param config
	 * @param jndiName
	 * @return
	 */
	public static EJBLocalHome getEJBLocalHome(EJBConfig config, String jndiName) {
		EJBLocalHome home = null;
		try {
			// Load EJB Configuration
			EJBConfig ejbConfig = config;
			if (ejbConfig == null) {
				ejbConfig = EJBUtils.getDefaultConfig();
			}
			Context context = ejbConfig.createContext();
			Object homeInterface = context.lookup(jndiName);
			home = (EJBLocalHome) PortableRemoteObject.narrow(homeInterface, homeInterface.getClass());
			context.close();
		} catch (NamingException namingException) {
			
		}
		return home;
	}
	
	/**
	 * Gets the EJBLocalObject for the given JndiName.
	 * @param jndiName
	 * @return
	 */
	public static EJBLocalObject getLocalInstance(String jndiName) {
		return getLocalInstance(null, jndiName);
	}
	
	/**
	 * Gets the EJBLocalObject for the given JndiName.
	 * @param config
	 * @param jndiName
	 * @return
	 */
	public static EJBLocalObject getLocalInstance(EJBConfig config, String jndiName) {
		// EJB Object
		EJBLocalObject instance = null;
		try {
			// Load EJB Configuration
			EJBConfig ejbConfig = config;
			if (ejbConfig == null) {
				ejbConfig = EJBUtils.getDefaultConfig();
			}
			// Create Context
			Context ctx = ejbConfig.createContext();
			// Look EJBHome Interface
			Object homeInterface = ctx.lookup(jndiName);
			// Close Context 
			ctx.close();

			// Get the Home Object
			Object home = PortableRemoteObject.narrow(homeInterface, homeInterface.getClass());
			Class homeClass = home.getClass();
			Method createMethod = homeClass.getDeclaredMethod("create", new Class[0]);
			// Create EJB Object
			instance = (EJBLocalObject) createMethod.invoke(home, new Object[0]);
		} catch (NamingException e) {
			// TODO throw CustomException
			throw new EJBException(e.getMessage(), e);
		} catch (NoSuchMethodException e) {
			// TODO throw CustomException
			throw new EJBException(e.getMessage(), e);
		} catch (IllegalAccessException e) {
			// TODO throw CustomException
			throw new EJBException(e.getMessage(), e);
		} catch (InvocationTargetException e) {
			// TODO throw CustomException
			throw new EJBException(e.getMessage(), e);
		}
		
		return instance;
	}	

	/**
	 * Get Default EJB Configuration
	 * @return EJB Configuration
	 */
	public static EJBConfig getDefaultConfig() {
		EJBConfig ejbConfig = new EJBConfig();
		ejbConfig.setInitialFactory(GlobalConfig.getProperty("java.naming.factory.initial"));
		ejbConfig.setFactoryUrlPkgs(GlobalConfig.getProperty("java.naming.factory.url.pkgs"));
		ejbConfig.setProviderUrl(GlobalConfig.getProperty("java.naming.provider.url"));
		return ejbConfig;
	}
}
